/*
 *  Licensed to the Apache Software Foundation (ASF) under one or more
 *  contributor license agreements.  See the NOTICE file distributed with
 *  this work for additional information regarding copyright ownership.
 *  The ASF licenses this file to You under the Apache License, Version 2.0
 *  (the "License"); you may not use this file except in compliance with
 *  the License.  You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 *  Unless required by applicable law or agreed to in writing, software
 *  distributed under the License is distributed on an "AS IS" BASIS,
 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *  See the License for the specific language governing permissions and
 *  limitations under the License.
 */

package org.apache.wikimark;

import java.io.IOException;
import java.io.Reader;
import java.util.Calendar;
import java.util.regex.Pattern;

import org.apache.wikimark.http.AbstractPostRequest;
import org.apache.wikimark.http.SimpleMultipartPostRequest;
import org.apache.wikimark.http.XFormPostRequest;

/**
 * The set of default Wiki drivers for popular Wiki engines
 * @author Sergey I. Salishev
 */
public final class DefaultDriverSet {
	
	private abstract static class RestrictedDriver extends Driver {
		private final String pattern;
		
		RestrictedDriver(String pattern) {
			this.pattern = pattern;
		}

		public AbstractPostRequest createRequest(String id, String text) {
			AbstractPostRequest request = createRequest(id);
			setId(request, id);
			if (text != null) {
				setText(request, text);
			}
			return request;
		}
		
		public int getPageSizeVariation() {
			return 0;
		}

		boolean matches(String mainPage) {
			Pattern p = Pattern.compile(pattern);
			return p.matcher(mainPage).find();
		}		


		AbstractPostRequest createRequest(String id) {
			return new SimpleMultipartPostRequest("");
		}
		

	    abstract void setId(AbstractPostRequest request, String id);

	    abstract void setText(AbstractPostRequest request, String text);
	    
	}
	
	static final class DokuWikiDriver extends RestrictedDriver {
		DokuWikiDriver() {
			super("title=\"Driven by DokuWiki\">");
		}
		
	    void setId(AbstractPostRequest request, String id) {
	        request.addParameter("id", id);        
	    }

	    void setText(AbstractPostRequest request, String text) {
	        request.addParameter("do", "save");
	        request.addParameter("summary", "none");
	        request.addParameter("wikitext", text);
	    }
	    
	    public String createHeader(String header, int level) {        
	        return createHeader(header, level, '=', 6, 2, true);
	    }

	    public String createRef(String link, String word) {
	        return "[[" + link + "|" + word + "]]";
	    }

	    public boolean requiresCamelCase() {
	        return false;
	    }


	}
	
	static final class HikiWikiDriver extends RestrictedDriver {
		HikiWikiDriver() {
			super("Generated by <a href=\"http://hikiwiki.org/\">Hiki</a>");
		}
		
	    public void setId(AbstractPostRequest request, String id) {
	        request.addParameter("p", id);        
	        request.addParameter("PageTitle", id);
	    }

	    public void setText(AbstractPostRequest request, String text) {
	        request.addParameter("update_timestamp", "on");
	        request.addParameter("keyword", "none");
	        request.addParameter("save", "Save");
	        request.addParameter("c", "save");
	        request.addParameter("contents", text);
	    }
	    
	    public String createHeader(String header, int level) {        
	        return createHeader(header, level, '=', 2, 6, true);
	    }

	    public String createRef(String link, String word) {
	        return "[[" + link + "|" + word + "]]";
	    }

	    public boolean requiresCamelCase() {
	        return false;
	    }
	    
	    @Override
	    AbstractPostRequest createRequest(String id) {
	    	return new XFormPostRequest("");
	    }

	}
	
	static final class RuwikiDriver extends RestrictedDriver {
		RuwikiDriver() {
			super("\\$Id: ruwiki.css");
		}
		
	    void setId(AbstractPostRequest request, String id) {     
	        request.addParameter("topic", id);
	    }

	    void setText(AbstractPostRequest request, String text) {
	    	request.addParameter("newpage", text);
	        request.addParameter("project", "Default");
	        request.addParameter("edcomment", "none");
	        request.addParameter("version", Long.toString(System
					.currentTimeMillis()));
	        //FIXME: should be "" but breaks JRuby
	        request.addParameter("save", " ");	        
	    }
	    
	    public String createHeader(String header, int level) {        
	        return createHeader(header, level, '=', 1, 6, false);
	    }

		public String createRef(String link, String word) {
	        return "[[" + link + "|" + word + "]]";
	    }

	    public boolean requiresCamelCase() {
	        return false;
	    }
	    
	    @Override
	    AbstractPostRequest createRequest(String id) {
	    	return new XFormPostRequest(id != null ? ("/Default/" + id) : "");
	    }

	}
	
	static final class MonkeyWikiDriver extends RestrictedDriver {
	    MonkeyWikiDriver() {
			super("<p id=\"credit\">Site powered by <a href=\"http://www.waywood.co.uk" +
					"/MonkeyWiki/\">MonkeyWiki</a></p>");
		}

		public String createRef(String link, String word) {
	        return link;
	    }

	    public String createHeader(String header, int level) {
	        return createHeader(header, level, '_', 2, 6, true);
	    }
	    
	    public boolean requiresCamelCase() {
	        return true;
	    }
	    
	    void setId(AbstractPostRequest request, String id) {
	        request.addParameter("page", id);
	    }

	    void setText(AbstractPostRequest request, String text) {
	        request.addParameter("action", "edit");
	        request.addParameter("save", "Save");
	        request.addParameter("newtext", text);
	        if (text.length() == 0) {
	        	request.addParameter("confirmdelete", "Yes");
	        }
	    }

	}
		
	static final class OddmuseWikiDriver extends RestrictedDriver {
	    OddmuseWikiDriver() {
			super("oddmuse.css");
		}

	    public String createRef(String link, String word) {
	        return "[[ " + link + " |"  + word + "]]";
	    }

	    public String createHeader(String header, int level) {
	        return createHeader(header, level, '=', 2, 6, true);
	    }

	    public boolean requiresCamelCase() {
	        return false;
	    }

	    void setId(AbstractPostRequest request, String id) {
	        request.addParameter("title", id);        
	    }

	    void setText(AbstractPostRequest request, String text) {
	        request.addParameter("text", text);
	        request.addParameter("summary", "none");
	    }
	}
	
    static final class MoinMoinWikiDriver extends RestrictedDriver {
        MoinMoinWikiDriver() {
            super("<a href=\"http://moinmo.in/\" title=\"This site uses the MoinMoin Wiki software.\">" +
                    "MoinMoin Powered</a>");
        }

        public String createRef(String link, String word) {
            return "[[ " + link + " |" + word + "]]";
        }

        public String createHeader(String header, int level) {
            return createHeader(header, level, '=', 1, 5, true);
        }

        public boolean requiresCamelCase() {
            return true;
        }

        void setId(AbstractPostRequest request, String id) {
            request.addParameter("pagename", id);
        }

        void setText(AbstractPostRequest request, String text) {
            if (text.length() == 0) {
                // delete
                request.addParameter("action", "DeletePage");
                request.addParameter("delete", "true");
            } else {
                // create
                request.addParameter("action", "edit");
                request.addParameter("savetext", text);
            }
        }

        @Override
        AbstractPostRequest createRequest(String id) {
            return new XFormPostRequest(id != null ? "/" + id : "");
        }
        
    }
    
    static final class MediaWikiDriver extends RestrictedDriver {
    	private final Calendar calendar = Calendar.getInstance();
    	
        MediaWikiDriver() {
            super("<a href=\"http://www.mediawiki.org/\">");            
        }

        void setId(AbstractPostRequest request, String id) {
            request.addParameter("title", id);
        }

        void setText(AbstractPostRequest request, String text) {
            if (text.length() == 0) {
                request.addParameter("action", "delete");
            } else {
                request.addParameter("action", "submit");                
            }
            StringBuilder stamp = new StringBuilder();
            
            stamp.append(calendar.get(Calendar.YEAR));
            stamp.append(calendar.get(Calendar.MONTH));
            stamp.append(calendar.get(Calendar.DAY_OF_MONTH));
            stamp.append(calendar.get(Calendar.HOUR));
            stamp.append(calendar.get(Calendar.MINUTE));
            stamp.append(calendar.get(Calendar.SECOND));
            
            request.addParameter("wpEdittime", stamp.toString());
            request.addParameter("wpStarttime", stamp.toString());
            
            request.addParameter("wpEditToken", "+\\");
            
            request.addParameter("wpTextbox1", text);
        }

        public String createHeader(String header, int level) {
            return createHeader(header, level, '=', 2, 6, true);
        }

        public String createRef(String link, String word) {
            return "[[" + link + "|" + word + "]]";
        }

        public boolean requiresCamelCase() {
            return false;
        }
        
        public int getPageSizeVariation() {
			return 100;
		}
    }
	/**
	 * Detects the Wiki engine based on the starting Wiki page
	 * @param is the stream of Wiki starting page
	 * @return the driver for the detected wiki engine 
	 * @throws IOException on stream reading error
	 * @throws IllegalArgumentException if no driver is found
	 */
	public static Driver findDriver(Reader is) throws IOException {
		String mainPage = readWhole(is);
    	for (Class<?> cl : DefaultDriverSet.class.getDeclaredClasses()) {
			try {
				RestrictedDriver value = (RestrictedDriver) cl.newInstance();
	    		if (value.matches(mainPage)) {
	    			return value;
	    		}
			} catch (Exception e) {
				continue;
			}
    	}
    	System.err.println(mainPage);
		throw new IllegalArgumentException(
				"no driver found for supplied start page");
    }
	
	private static String readWhole(Reader is) throws IOException {
		char[] buf = new char[1 << 10];
		int length = is.read(buf);
		StringBuilder res = new StringBuilder();
		while(length >= 0) {
			res.append(buf, 0, length);
			length = is.read(buf); 
		}
		return res.toString();
	}
}
